import { BaseConverter } from './BaseConverter.js';
import { ParametersConverter } from './ParametersConverter.js';
import { SchemaConverter } from './SchemaConverter.js';
import { SecurityConverter } from './SecurityConverter.js';
import { ServersConverter } from './ServersConverter.js';
import { InvalidSchemaError } from './errors.js';
import { generateExampleFromSchema } from './generateExampleFromSchema.js';
export class OpenApiToEndpointConverter extends BaseConverter {
    constructor(document, path, method, safeParse = false) {
        super(safeParse);
        this.document = document;
        this.path = path;
        this.method = method;
        this.safeParse = safeParse;
    }
    convert() {
        var _a, _b, _c, _d, _e;
        const paths = this.document.paths;
        if (paths === undefined) {
            throw new InvalidSchemaError(['#'], 'paths not defined');
        }
        const pathObject = paths[this.path];
        if (pathObject === undefined) {
            throw new InvalidSchemaError(['#', 'paths'], `path not defined: ${this.path}`);
        }
        const operationObject = pathObject[this.method];
        if (operationObject === undefined) {
            throw new InvalidSchemaError(['#', 'paths', this.path], `operation does not exist: ${this.method}`);
        }
        const securityRequirements = (_a = operationObject.security) !== null && _a !== void 0 ? _a : this.document.security;
        const securitySchemes = (_b = this.document.components) === null || _b === void 0 ? void 0 : _b.securitySchemes;
        const security = SecurityConverter.convert(securityRequirements, securitySchemes, this.safeParse);
        const pathParameters = pathObject.parameters;
        const operationParameters = operationObject.parameters;
        const parameters = ParametersConverter.convert(this.method, pathParameters, operationParameters, ['#', 'paths', this.path], this.safeParse);
        const servers = ServersConverter.convert((_d = (_c = operationObject.servers) !== null && _c !== void 0 ? _c : pathObject.servers) !== null && _d !== void 0 ? _d : this.document.servers);
        // title is a bit too specific to take from the path object
        const title = operationObject.summary;
        const description = (_e = operationObject.description) !== null && _e !== void 0 ? _e : pathObject.description;
        const requestBody = operationObject.requestBody;
        const body = this.convertContent(['#', 'paths', this.path, this.method, 'requestBody', 'content'], requestBody === null || requestBody === void 0 ? void 0 : requestBody.content, 'request', requestBody === null || requestBody === void 0 ? void 0 : requestBody.required);
        const deprecated = !!operationObject.deprecated;
        const codeSamples = this.convertCodeSamples(['#', 'paths', this.path, this.method], operationObject);
        const response = this.convertResponses(['#', 'paths', this.path, this.method, 'responses'], operationObject.responses);
        return {
            title,
            description,
            path: this.path,
            method: this.method,
            servers,
            request: {
                security,
                parameters,
                body,
                codeSamples,
            },
            response,
            deprecated,
        };
    }
    convertContent(debugPath, content, location, required) {
        if (content === undefined) {
            return {};
        }
        const newEntries = Object.entries(content).map(([contentType, mediaObject]) => {
            const schemaArray = SchemaConverter.convert({
                schema: mediaObject.schema,
                path: [...debugPath, contentType, 'schema'],
                required,
                location,
                contentType,
                safeParse: this.safeParse,
            });
            const examples = this.convertExamples(mediaObject.examples, mediaObject.example, schemaArray);
            return [contentType, { schemaArray, examples }];
        });
        return Object.fromEntries(newEntries);
    }
    convertExamples(examples, example, schemaArray) {
        if (examples && Object.values(examples).some(({ value }) => value !== undefined)) {
            return Object.fromEntries(Object.entries(examples)
                .filter(([_, { value }]) => value !== undefined)
                .map(([key, example]) => [
                key,
                {
                    summary: example.summary,
                    description: example.description,
                    value: example.value,
                },
            ]));
        }
        if (example !== undefined) {
            return { example: { value: example } };
        }
        return { example: { value: generateExampleFromSchema(schemaArray[0]) } };
    }
    convertResponses(debugPath, responses) {
        const newEntries = Object.entries(responses).map(([statusCode, response]) => [
            statusCode,
            this.convertContent([...debugPath, statusCode, 'content'], response.content, 'response'),
        ]);
        return Object.fromEntries(newEntries);
    }
    convertCodeSamples(debugPath, operation) {
        let key;
        let rawCodeSamples;
        if ('x-codeSamples' in operation) {
            rawCodeSamples = operation['x-codeSamples'];
            key = 'x-codeSamples';
        }
        else if ('x-code-samples' in operation) {
            rawCodeSamples = operation['x-code-samples'];
            key = 'x-code-samples';
        }
        else {
            return undefined;
        }
        if (!Array.isArray(rawCodeSamples)) {
            this.handleNewError(InvalidSchemaError, [...debugPath, key], `${key} must be an array`);
            return;
        }
        const codeSamples = [];
        rawCodeSamples.forEach((codeSample) => {
            if (!codeSample ||
                typeof codeSample !== 'object' ||
                !('source' in codeSample) ||
                typeof codeSample.source !== 'string' ||
                !('lang' in codeSample) ||
                typeof codeSample.lang !== 'string') {
                this.handleNewError(InvalidSchemaError, [...debugPath, key], 'invalid code sample');
                return;
            }
            codeSamples.push({
                label: 'label' in codeSample && typeof codeSample.label === 'string'
                    ? codeSample.label
                    : undefined,
                lang: codeSample['lang'].toLowerCase(),
                source: codeSample['source'],
            });
        });
        return codeSamples;
    }
    static convert(spec, path, method, safeParse) {
        return new OpenApiToEndpointConverter(spec, path, method, safeParse).convert();
    }
}
